/**
 * @brief   XX项目 GPS驱动
 * @author 高浩越 <1032993493@qq.com>
 * @note
 *       使用的GPS模块型号为：
 *           ublox LEA-6T
 *
 *       硬件连接方式为：
 *           串口        <--->  MCU
 *           秒脉冲GPIO  <--->  MCU
 *
 *       主要功能有：
 *           1.提供GPS定位
 *           2.提供秒脉冲中断给MCU
 *           3.通过串口配置GPS
 *           4.系统时钟校准
 */
#include "gps.h"

__IO uint8_t rx_count = 0;
__IO uint8_t receive_flag = 0;
/** Config Timepulse0 pin
        Unlock to GPS: Period 1000000us, Length 0
        Lock to GPS: Period 1000000us, Length 500000us
*/
const unsigned char UBX_MSG_DATA_SET_TIMEPULSE_0[] = {
    0x00,                   // Timepulse selection (0 = TIMEPULSE, 1 =TIMEPULSE2)
    0x01,                   // Reserved
    0x00, 0x00,             // Reserved
    0x00, 0x00,             // Antenna cable delay
    0x00, 0x00,             // RF group delay
    0x00, 0x00, 0x00, 0x00, // Frequency or period time, depending on setting of bit ’isFreq’
    0x01, 0x00, 0x00, 0x00, // Frequency or period time when locked to GPS time, only used if ’lockedOtherSet’ is set
    0x00, 0x00, 0x00, 0x00, // Pulse length or duty cycle, depending on ’isLength’
    0x00, 0x00, 0X00, 0X80, // Pulse length or duty cycle when locked to GPS time, only used if ’lockedOtherSet’ is set
    0x00, 0x00, 0x00, 0x00, // User configurable timepulse delay
    0xef, 0x00, 0x00, 0x00  // configuration flags (see graphic below)
};
/** Config Timepulse1 pin
        Unlock to GPS: Period 1000000us, Length 0
        Lock to GPS: Frequency 1024000Hz
*/
unsigned char UBX_MSG_DATA_SET_TIMEPULSE_1[] = {
    0x01,                   // Timepulse selection (0 = TIMEPULSE, 1 =TIMEPULSE2)
    0x01,                   // Reserved
    0x00, 0x00,             // Reserved
    0x00, 0x00,             // Antenna cable delay
    0x00, 0x00,             // RF group delay
    0x00, 0xA0, 0x0F, 0x00, // Frequency or period time, depending on setting of bit ’isFreq’
    0x00, 0xA0, 0x0F, 0x00, // 1.024MHz=0x000FA000 Frequency or period time when locked to GPS time, only used if ’lockedOtherSet’ is set
    0x00, 0x00, 0x00, 0x80, // Pulse length or duty cycle, depending on ’isLength’
    0x00, 0x00, 0X00, 0X80, // Pulse length or duty cycle when locked to GPS time, only used if ’lockedOtherSet’ is set
    0x00, 0x00, 0x00, 0x00, // User configurable timepulse delay
    0xef, 0x00, 0x00, 0x00  // configuration flags (see graphic below)
};

static void gps_uart_receive(unsigned char *data_buf, uint32_t length)
{
    uint8_t i;
    for (i = 0; i < length; i++)
    {
        while (RESET == usart_flag_get(GPS_UART, USART_FLAG_RBNE));
        data_buf[i] = usart_data_receive(GPS_UART);
    }
}

static int gps_uart_send(unsigned char *data_buf, uint32_t length)
{
    uint32_t i = 0;

    for (i = 0; i < length; i++)
    {
        usart_data_transmit(GPS_UART, data_buf[i]);
        while (usart_flag_get(GPS_UART, USART_FLAG_TBE) == RESET);
    }

    return 0;
}
/**
 * @brief GPS硬復位
 */
static  int gps_reset(void)
{
    int ret;
    //GPS模块复位引脚需1S左右的一个脉冲信号启动

    gpio_bit_reset(GPIOB,GPS_RST_PIN);
    rt_thread_mdelay(1000);//遵循优化原则
    gpio_bit_set(GPIOB,GPS_RST_PIN);
	
    ret = gpio_output_bit_get(GPIOB,GPS_RST_PIN);
    if(!ret){
        rt_kprintf("GPS_RST_PIN pin state error,GPS don't work,please check\r\n");
        return RT_ERROR;
    }
    rt_kprintf("GPS LEA-6T reset complete,start work\r\n");

    return RT_EOK;
}
static int gps_power_on(void)
{
    int ret;

    //先使能GPS供电
		gpio_bit_reset(GPIOB,GPS_PWR_EN);
	  ret = gpio_output_bit_get(GPIOB,GPS_PWR_EN);
    if (ret)
    {
        rt_kprintf("GPS_PWR_EN pin state error,GPS don't work,please check\r\n");
        return RT_ERROR;
    }
    rt_kprintf("GPS LEA-6T power on\r\n");

    return RT_EOK;
}

static int gps_uart_init(void)
{
    /* enable GPIO clock ????1??IO?? */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART1);

    /* connect port to USARTx_Tx */
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);

    /* connect port to USARTx_Rx */
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);

    /* USART configure */
    usart_deinit(USART1);
    usart_baudrate_set(USART1, 9600);                          //???????
    usart_word_length_set(USART1, USART_WL_8BIT);              //???8?????
    usart_stop_bit_set(USART1, USART_STB_1BIT);                //?????
    usart_parity_config(USART1, USART_PM_NONE);                //??????
    usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); //????????
    usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); //????????
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);        //????
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
		usart_dma_receive_config(USART1, USART_DENR_ENABLE);
    usart_enable(USART1); //????0
		rt_kprintf("GPS LEA-6T UART INIT DOWN\r\n");
    return 0;
}
static int gps_gpio_init(void)
{
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_GPIOD);
	rcu_periph_clock_enable(RCU_GPIOC);

	gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPS_PWR_EN);
	gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPS_RST_PIN);
	gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPS_SYNC_PIN);
	gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPS_1PPS_PIN);

	return 0;
}
/**
 * @brief generate ubx protocol msg, which could send to ubx then.
 * @param msg_class for ubx protocol
 * @param msg_id    for ubx protocol
 * @param data       the content of msg
 * @param data_len
 * @param buf       the buffer to store the generated msg
 * @param buf_len
 *
 * @return actual length of the msg
 */
int ubx_generate(unsigned int msg_class, unsigned int msg_id, const unsigned char *data, size_t data_len,
                 unsigned char *buf, size_t buf_len)
{
    unsigned char CK_A, CK_B;
    size_t count;
    size_t i;

    count = 0;
    /* check */
    if (buf == NULL)
        return -RT_ERROR;
    if (buf_len < 6)
        return -RT_ERROR;

    buf[0] = 0xb5;
    buf[1] = 0x62;

    CK_A = CK_B = 0;
    buf[2] = msg_class;
    buf[3] = msg_id;
    buf[4] = data_len & 0xff;
    buf[5] = (data_len >> 8) & 0xff;

    count += 6;

    if (data != NULL && data_len != 0)
        (void)rt_memcpy(&buf[6], data, data_len);
    count += data_len;

    /* calculate CRC */
    for (i = 2; i < 6; i++)
    {
        CK_A += buf[i];
        CK_B += CK_A;
    }
    if (data != NULL)
        for (i = 0; i < data_len; i++)
        {
            CK_A += data[i];
            CK_B += CK_A;
        }

    buf[6 + data_len] = CK_A;
    buf[7 + data_len] = CK_B;
    count += 2;

    return count;
}
/**
 * @brief GPS模块上电后，通过ublox协议，配置GPS
 */
static int setup_gps_after_reset(void)
{
    int ret;
		int delay;
    unsigned char buf[64] = {0};

    ret = ubx_generate(UBX_MSG_CLASS_CFG, UBX_MSG_ID__CFG_TP5, UBX_MSG_DATA_SET_TIMEPULSE_0,
                       sizeof(UBX_MSG_DATA_SET_TIMEPULSE_0), buf, sizeof(buf));
    if (ret < 0)
    {
        rt_kprintf("ubx_generate UBX_MSG_DATA_SET_TIMEPULSE_0 failed\r\n");
        return  -RT_ERROR;
    }
	
		for(delay = 1000000; delay > 0; delay--)//必须加延时等待GPS模块正常启动后才能下发设置命令
		{
				__NOP();
				__NOP();
				__NOP();
				__NOP();
				__NOP();
		}
    //向GPS模块发送配置命令
    ret = gps_uart_send(buf,sizeof(buf));
    if (ret < 0)
    {
        rt_kprintf("GPS uart send msg failed\r\n");
        return  RT_ERROR;
    }

    ret = ubx_generate(UBX_MSG_CLASS_CFG, UBX_MSG_ID__CFG_TP5, UBX_MSG_DATA_SET_TIMEPULSE_1,
                       sizeof(UBX_MSG_DATA_SET_TIMEPULSE_1), buf, sizeof(buf));
    if (ret < 0)
    {
        rt_kprintf("ubx_generate UBX_MSG_DATA_SET_TIMEPULSE_0 failed\r\n");
        return  RT_ERROR;
    }

    ret = gps_uart_send(buf,sizeof(buf));
		
    if (ret < 0)
    {
        rt_kprintf("GPS uart send msg failed\r\n");
        return  RT_ERROR;
    }

    return  RT_EOK;
}

char rxbuffer[3000];
/*!
    \brief      configure NVIC
    \param[in]  none
    \param[out] none
    \retval     none
*/
void nvic_config(void)
{
    nvic_irq_enable(USART1_IRQn, 1, 0);
}

/*!
    \brief      configure USART DMA
    \param[in]  none
    \param[out] none
    \retval     none
*/
void dma_config(void)
{
    dma_parameter_struct dma_init_struct;
    
    rcu_periph_clock_enable(RCU_DMA0);
    
    /* deinitialize DMA channel4 (USART0 rx) */
		dma_deinit(DMA0, DMA_CH5);
		dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
		dma_init_struct.memory_addr = (uint32_t)rxbuffer;
		dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
		dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
		dma_init_struct.number = sizeof(rxbuffer) - 1;
		dma_init_struct.periph_addr = USART1_DATA_ADDRESS;
		dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
		dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
		dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
		dma_init(DMA0, DMA_CH5, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH5);
    /* enable DMA channel4 */
    dma_channel_enable(DMA0, DMA_CH5);
}
/*!
    \brief      initialize the EXTI configuration of the 1pps pin
    \param[in]  none
    \param[out] none
    \retval     none
*/
void gps_exti_init(void)
{
    /* enable the AF clock */
    rcu_periph_clock_enable(RCU_AF);
    /* enable and set key EXTI interrupt to the specified priority */
    nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
    nvic_irq_enable(EXTI2_IRQn, 2U, 2U);

    /* connect key EXTI line to key GPIO pin */
    gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOD, GPIO_PIN_SOURCE_2);

    /* configure key EXTI line */
    exti_init(EXTI_2, EXTI_INTERRUPT, EXTI_TRIG_RISING);
    exti_interrupt_flag_clear(EXTI_2);
}
struct minmea_date rmc_date={0};
struct minmea_time rmc_time={0};
/**
 * @brief 返回GPRMC中获取到的时间,格式：2022-06-22:08:00
 */
struct tm get_gps_time(void){
    struct tm ret;
    ret.tm_year = rmc_date.year+2000;
    ret.tm_mon = rmc_date.month;
    ret.tm_mday = rmc_date.day;
    ret.tm_hour = rmc_time.hours+8;
    ret.tm_min = rmc_time.minutes;
    ret.tm_sec = rmc_time.seconds;
    return ret;
}

static rt_event_t  gps_nmea_rmc_update_event;   //nmea RMC报文：UTC时间、经纬度、年月日
#define _GPS_NMEA_RMC_UPDATE     1<<0
/**
 * @brief gps串口任务，循环处理收到的数据
 *
 *
 * @note
         * 串口收到的数据有两种格式：
         * 1.标准NMEA格式，帧头 '$' 帧尾 '\n'
         * 2.ublox私有协议格式， 帧头 '0xB5 0x62' 帧尾 无
 */
void gps_uart_task(void *param)
{
	  for(;;)
  {
			if(1 == receive_flag){
					receive_flag = 0;
			}
			if(rxbuffer[0] == '$'){
				do{
						if(!rt_strncmp(rxbuffer, "$GPRMC", rt_strlen("$GPRMC")))
						{
						//$GPRMC,072656.00,A,3032.54872,N,10404.36302,E,0.195,,080622,,,A*78
						struct minmea_sentence_rmc frame;
						if (minmea_parse_rmc(&frame, rxbuffer))
							{
								register rt_base_t temp;
								temp = rt_hw_interrupt_disable();
								rmc_date = frame.date;
								rmc_time = frame.time;
								rt_hw_interrupt_enable(temp);
								rt_event_send(gps_nmea_rmc_update_event, _GPS_NMEA_RMC_UPDATE);
							}
						}
				}while(0);
			}
	}
}
int gps_init(void)
{
	int ret;
	struct tm time;
	dma_parameter_struct dma_init_struct;
	/*gps uart gpio init*/
	gps_gpio_init();
	/*gps power on*/
	gps_power_on();
	/*gps nvic enable*/
	nvic_config();
	/*gps uart dma config*/
	dma_config();
	/*gps uart config 9600 baud_rate enable uart*/
	gps_uart_init();
	/*gps uart 1pps pin interrupt config*/
	gps_exti_init();
	/*gps hardware reset*/
	gps_reset();
	/*gps config timepulse1 and timepulse2 ouptput frequence*/
	ret = setup_gps_after_reset();
	if(ret){
		rt_kprintf("GPS config failed\r\n");
		return RT_ERROR;
	}
	/*wait IDLEF set and clear it*/
//	while(RESET == usart_flag_get(GPS_UART, USART_FLAG_IDLEF));
//	usart_flag_clear(GPS_UART, USART_FLAG_IDLEF);
	usart_interrupt_enable(GPS_UART, USART_INT_IDLE);
	
	gps_nmea_rmc_update_event = rt_event_create("gps_nmea_rmc_update", RT_IPC_FLAG_PRIO);
  if(gps_nmea_rmc_update_event == NULL)
  {
      rt_kprintf("gps_uart_data_mq create failed!\n");
      return -1;
  }
	
	rt_thread_t thread;
  thread = rt_thread_create("uart_task",gps_uart_task,RT_NULL,1024, 21,20);
  RT_ASSERT(thread);
  rt_thread_startup(thread);
	
	return RT_EOK;
}

//INIT_APP_EXPORT(gps_init);
MSH_CMD_EXPORT(gps_init, gps drv test); /*  INIT_BOARD_EXPORT */
